package server.connector.http;



import org.apache.catalina.util.Enumerator;
import server.connector.RequestStream;
import server.util.ParameterMap;
import server.util.RequestUtil;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.security.Principal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class HttpRequest implements HttpServletRequest {
    private InputStream input;
    private String method;
    private String requestURI;
    private String protocol;
    //查询字符串
    private String queryString;
    //是否解析过参数
    protected boolean parsed = false;
    //存放参数
    protected ParameterMap<String, String[]> parameters = null;

    //解析参数
    protected void parseParameters() {
        //已经解析过，直接返回
        if (parsed) {
            return;
        }
        if (parameters == null)
            parameters = new ParameterMap<>();
        //开始解析，卸锁
        parameters.setLocked(false);
        String encoding = getCharacterEncoding();
        //默认编码为ISO-8859-1
        if (encoding == null)
            encoding = "ISO-8859-1";
        //解析查询字符串
        try {
            RequestUtil.parseParameters(parameters, queryString, encoding);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //post请求的请求体中也可以含有参数(当content-type=application/x-www-form-urlencode且content-length>0时)
        String contentType = (this.contentType == null? "" : this.contentType);
        //拿到主类型
        int index = contentType.indexOf(';');
        if (index >= 0) {
            contentType = contentType.substring(0, index);
        }
        contentType = contentType.trim();
        if ("POST".equals(method) && contentLength > 0 && "application/x-www-form-urlencoded".equals(contentType)) {
            try {
                int max = contentLength;
                int len = 0;
                byte[] buffer = new byte[contentLength];
                //一个更安全的输入流
                ServletInputStream is = getInputStream();
                //读请求体的内容
                while (len < max) {
                    int next = is.read(buffer, len, max - len);
                    if (next <= 0)
                        break;
                    len += next;
                }
                is.close();
                if (len < max)
                    throw new RuntimeException("Content length mismatch");
                //解析参数
                RequestUtil.parseParameters(parameters, buffer, encoding);
            } catch (UnsupportedEncodingException ue) {
                ;
            } catch (IOException e) {
                throw new RuntimeException("Content read fail");
            }
        }
        //解析结束，设置解析标志，上锁
        parsed = true;
        parameters.setLocked(true);
    }



    //url上的sessionID
    private String requestedSessionId;
    //sessionID是否在url上
    private boolean sessionIdInRequestedURL;
    //sessionID是否在cookie上
    private boolean sessionIdInRequestedCookie;
    //存放所有的请求头
    protected HashMap<String, ArrayList<String>> headers = new HashMap<>();
    //添加请求头
    public void addHeader(String name, String value) {
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values == null) {
               values = new ArrayList<>();
               headers.put(name, values);
            }
            values.add(value);
        }
    }
    //内容长度
    private int contentLength;
    //内容类型
    private String contentType;
    //存放cookie
    protected ArrayList<Cookie> cookies = new ArrayList<>();
    //添加cookie
    public void addCookie(Cookie cookie) {
        synchronized (cookies) {
            cookies.add(cookie);
        }
    }

    //构造器
    public HttpRequest(InputStream input) {
        this.input = input;
    }

    public InputStream getInput() {
        return this.input;
    }
    //setter
    public void setMethod(String method) {
        this.method = method;
    }
    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }
    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }
    public void setQueryString(String queryString) {
        this.queryString = queryString;
    }
    public void setRequestedSessionId(String requestedSessionId) {
        this.requestedSessionId = requestedSessionId;
    }
    public void setSessionIdInRequestedURL(boolean flag) {
        this.sessionIdInRequestedURL = flag;
    }
    public void setSessionIdInRequestedCookie(boolean flag) {
        this.sessionIdInRequestedCookie = false;
    }
    public void setContentLength(int length) {
        this.contentLength = length;
    }
    public void setContentType(String type) {
        this.contentType = type;
    }
    //以下方法实现了HttpServletRequest

    //-------------------------------------------------------------------------------------------------
    //存放属性
    protected HashMap<String, Object> attributes = new HashMap<>();
    //根据属性名获取属性值 (例子:servlet1从数据库查数据后用setAttribute存放数据后转发到jsp，jsp用getAttribute获取数据渲染)
    @Override
    public Object getAttribute(String name) {
        synchronized (attributes) {
            return attributes.get(name);
        }
    }
    //获取所有的属性名
    @Override
    public Enumeration getAttributeNames() {
        synchronized (attributes) {
            return new Enumerator(attributes.keySet());
        }
    }
    //设置属性值
    @Override
    public void setAttribute(String name, Object obj) {
        synchronized (attributes) {
            attributes.put(name, obj);
        }
    }
    //删除属性
    @Override
    public void removeAttribute(String name) {
        synchronized (attributes) {
            attributes.remove(name);
        }
    }

    //-------------------------------------------------------------------------------------------------
    //根据参数名获取参数
    //服务器处理完后Servlet只能读parameters，所有不用加锁
    @Override
    public String getParameter(String name) {
        parseParameters();
        String[] values = parameters.get(name);
        if (values != null && values.length > 0) {
            return values[0];
        }
        return null;
    }
    //获取存参数的map
    @Override
    public Map<String, String[]> getParameterMap() {
        parseParameters();
        return this.parameters;
    }
    //获取所有参数名
    @Override
    public Enumeration getParameterNames() {
        parseParameters();
        return (new Enumerator(parameters.keySet()));
    }
    //获取参数的所有参数值
    @Override
    public String[] getParameterValues(String name) {
        parseParameters();
        String[] values = parameters.get(name);
        return values;
    }
    //-------------------------------------------------------------------------------------------------
    //获取请求方法
    @Override
    public String getMethod() {
        return method;
    }
    //获取请求uri
    @Override
    public String getRequestURI() {
        return requestURI;
    }
    //获取请求协议版本
    @Override
    public String getProtocol() {
        return protocol;
    }
    //获取查询字符串
    @Override
    public String getQueryString() {
        return queryString;
    }
    //获取jsessionid
    @Override
    public String getRequestedSessionId() {
        return this.requestedSessionId;
    }
    //根据名字获取请求头的值 (感觉解析完后Servlet只读不写不用加锁)
    @Override
    public String getHeader(String name) {
        //输入大小写不敏感
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values != null && values.size() > 0) {
                return values.get(0);
            }
            return null;
        }
    }
    //获取全部请求头的名字
    @Override
    public Enumeration getHeaderNames() {
        synchronized (headers) {
            return new Enumerator(headers.keySet());
        }
    }
    //空集合
    protected static ArrayList empty = new ArrayList();
    //根据name获取其值得集合
    @Override
    public Enumeration getHeaders(String name) {
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values == null) {
                return new Enumerator(empty);
            }
            return new Enumerator(values);
        }
    }
    //获取给定name对应int值
    @Override
    public int getIntHeader(String name) {
        name = name.toLowerCase();
        String value = getHeader(name);
        if (value == null) {
            return -1;
        }
        return Integer.parseInt(value);
    }

    //三种时间格式
    protected SimpleDateFormat formats[] = {
            new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
    };
    //获取给定name对应值解析为时间的时间戳
    @Override
    public long getDateHeader(String name) {
        String value = getHeader(name);
        if (value == null)
            return -1;
        //不加" "可能会有bug (Work around a bug in SimpleDateFormat in pre-JDK1.2b4(Bug Parade bug #4106807))
        value += " ";
        for (int i = 0; i < formats.length; i++) {
            try {
                Date date = formats[i].parse(value);
                return date.getTime();
            } catch (ParseException e) {

            }
        }
        throw new IllegalArgumentException(value);
    }
    //获取cookie数组
    @Override
    public Cookie[] getCookies() {
        synchronized (cookies) {
            if (cookies.size() < 1) {
                return null;
            }
            Cookie[] cs = new Cookie[cookies.size()];
            return cookies.toArray(cs);
        }
    }
    //获取内容长度
    @Override
    public int getContentLength() {
        return contentLength ;
    }
    //获取内容类型
    @Override
    public String getContentType() {
        return contentType;
    }

    //从socket获取的输入流封装成的ServletInputStream(强制读取的字节数不超过给定的length)
    protected ServletInputStream stream = null;
    //将从socket获取的输入流封装成的ServletInputStream封装成BufferedReader(按规定编码进行字节字符转换)
    protected BufferedReader reader = null;
    //获取封装后的字节输入流 与getReader()互斥，二者都调用时会报错
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (reader != null)
            throw new IllegalStateException("getReader has been called");
        if (stream == null)
            stream = createInputStream();
        return stream;
    }
    //获取封装后的字符输入流 与getInputStream()互斥，二者都调用时会报错
    @Override
    public BufferedReader getReader() throws IOException {
        if (stream != null)
            throw new IllegalStateException("getInputStream has been called.");
        if (reader == null) {
            String encoding = getCharacterEncoding();
            if (encoding == null)
                encoding = "ISO-8859-1";
            reader = new BufferedReader(new InputStreamReader(createInputStream(), encoding));
        }
        return reader;
    }
    // 创建特殊的输入流
    // 对从socket获取的输入流进行封装，强制读取的字节数不超过给定的length
    // 若数据未读完就关闭流，该流会自动将数据读够length长度
    public ServletInputStream createInputStream() throws IOException {
        return (new RequestStream(this));
    }

    //-------------------------------------------------------------------------------------------------
    //未实现的方法

    //获取字符集编码
    public String getCharacterEncoding() {
        return null;
    }
    //设置字符集编码
    @Override
    public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException {

    }

    //获取URL
    @Override
    public StringBuffer getRequestURL() {
        return null;
    }
    //获取session
    @Override
    public HttpSession getSession() {
        return null;
    }
    //获取session
    @Override
    public HttpSession getSession(boolean b) {
        return null;
    }

    //应用名路径
    protected String contextPath = "";
    //获取应用路径
    //例1:应用名为app1 请求uri为/app1/xxx/xxx... 返回/+应用名，即/app1
    //例2:应用名为空 请求uri为/xxx/xxxxx/xxx... 返回空字符串""
    @Override
    public String getContextPath() {
        return contextPath;
    }

    //请求的实际URL相对于请求的serlvet的url的路径
    protected String pathInfo = null;
    //获取请求的实际URL相对于请求的serlvet的url的路径
    //例:Servlet的映射配置:<url-pattern>/t/*</url-pattern>
    //访问路径:http://localhost:8086/t/asdf/gfd?a=fsd
    //结果:/asdf/gfd
    @Override
    public String getPathInfo() {
        return pathInfo;
    }
    //获取请求的实际URL相对于请求的serlvet的url的路径
    //例:Servlet的映射配置:<url-pattern>/t/*</url-pattern>
    //访问路径:http://localhost:8086/t/asdf/gfd?a=fsd
    //结果:E:\WebServer\myServer\webTest\out\artifacts\webTest_war_exploded\asdf\gfd
    @Override
    public String getPathTranslated() {
        return null;
    }
    //获取请求的实际URL相对于请求的serlvet的url的路径
    //例:Servlet的映射配置:<url-pattern>/t/*</url-pattern>
    //访问路径:http://localhost:8086/t/asdf/gfd?a=fsd
    //结果:/t
    @Override
    public String getServletPath() {
        return null;
    }
    //获取请求的实际URL相对于请求的serlvet的url的路径
    //例:Servlet的映射配置:<url-pattern>/t/*</url-pattern>
    //访问路径:http://localhost:8086/t/asdf/gfd?a=fsd
    //getRealPath("/")结果:E:\WebServer\myServer\webTest\out\artifacts\webTest_war_exploded\
    @Override
    public String getRealPath(String path) {
        return null;
    }


    //获取请求需要的语言地区(Accept-Language: zh-CN,zh;q=0.9)
    @Override
    public Locale getLocale() {
        return null;
    }
    //获取请求需要的所有语言地区(Accept-Language: zh-CN,zh;q=0.9)
    @Override
    public Enumeration getLocales() {
        return null;
    }
    //获取服务器主机名
    @Override
    public String getServerName() {
        return null;
    }
    //获取服务器端口
    @Override
    public int getServerPort() {
        return 0;
    }
    //获取使用的协议(只有协议名没有协议版本：如http)
    @Override
    public String getScheme() {
        return null;
    }
    //获取客户端主机名
    @Override
    public String getRemoteHost() {
        return null;
    }
    //获取客户端IP地址
    @Override
    public String getRemoteAddr() {
        return null;
    }

    //请求转发
    @Override
    public RequestDispatcher getRequestDispatcher(String path) {
        return null;
    }
    //----------------
    //不懂什么作用+未实现的方法
    //不太懂为啥一直是flase
    @Override
    public boolean isRequestedSessionIdFromCookie() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromUrl() {
        return isRequestedSessionIdFromURL();
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        return false;
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public boolean isUserInRole(String s) {
        return false;
    }

    @Override
    public String getAuthType() {
        return null;
    }

    @Override
    public String getRemoteUser() {
        return null;
    }

    @Override
    public Principal getUserPrincipal() {
        return null;
    }

    //-------------------------------------------------------------------------------------------------
}
